// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/proxy/mock_proxy_resolver.h"

#include <utility>

#include "base/logging.h"
#include "base/memory/ptr_util.h"

namespace net {

MockAsyncProxyResolver::Request::Request(MockAsyncProxyResolver* resolver,
    const GURL& url,
    ProxyInfo* results,
    const CompletionCallback& callback)
    : resolver_(resolver)
    , url_(url)
    , results_(results)
    , callback_(callback)
{
}

void MockAsyncProxyResolver::Request::CompleteNow(int rv)
{
    CompletionCallback callback = callback_;

    // May delete |this|.
    resolver_->RemovePendingRequest(this);

    callback.Run(rv);
}

MockAsyncProxyResolver::Request::~Request() { }

MockAsyncProxyResolver::~MockAsyncProxyResolver() { }

int MockAsyncProxyResolver::GetProxyForURL(const GURL& url,
    ProxyInfo* results,
    const CompletionCallback& callback,
    RequestHandle* request_handle,
    const BoundNetLog& /*net_log*/)
{
    scoped_refptr<Request> request = new Request(this, url, results, callback);
    pending_requests_.push_back(request);

    if (request_handle)
        *request_handle = reinterpret_cast<RequestHandle>(request.get());

    // Test code completes the request by calling request->CompleteNow().
    return ERR_IO_PENDING;
}

void MockAsyncProxyResolver::CancelRequest(RequestHandle request_handle)
{
    scoped_refptr<Request> request = reinterpret_cast<Request*>(request_handle);
    cancelled_requests_.push_back(request);
    RemovePendingRequest(request.get());
}

LoadState MockAsyncProxyResolver::GetLoadState(
    RequestHandle request_handle) const
{
    return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
}

void MockAsyncProxyResolver::RemovePendingRequest(Request* request)
{
    RequestsList::iterator it = std::find(
        pending_requests_.begin(), pending_requests_.end(), request);
    DCHECK(it != pending_requests_.end());
    pending_requests_.erase(it);
}

MockAsyncProxyResolver::MockAsyncProxyResolver()
{
}

MockAsyncProxyResolverFactory::Request::Request(
    MockAsyncProxyResolverFactory* factory,
    const scoped_refptr<ProxyResolverScriptData>& script_data,
    std::unique_ptr<ProxyResolver>* resolver,
    const CompletionCallback& callback)
    : factory_(factory)
    , script_data_(script_data)
    , resolver_(resolver)
    , callback_(callback)
{
}

MockAsyncProxyResolverFactory::Request::~Request()
{
}

void MockAsyncProxyResolverFactory::Request::CompleteNow(
    int rv,
    std::unique_ptr<ProxyResolver> resolver)
{
    *resolver_ = std::move(resolver);

    // RemovePendingRequest may remove the last external reference to |this|.
    scoped_refptr<MockAsyncProxyResolverFactory::Request> keep_alive(this);
    factory_->RemovePendingRequest(this);
    factory_ = nullptr;
    callback_.Run(rv);
}

void MockAsyncProxyResolverFactory::Request::CompleteNowWithForwarder(
    int rv,
    ProxyResolver* resolver)
{
    DCHECK(resolver);
    CompleteNow(rv, base::WrapUnique(new ForwardingProxyResolver(resolver)));
}

void MockAsyncProxyResolverFactory::Request::FactoryDestroyed()
{
    factory_ = nullptr;
}

class MockAsyncProxyResolverFactory::Job
    : public ProxyResolverFactory::Request {
public:
    explicit Job(
        const scoped_refptr<MockAsyncProxyResolverFactory::Request>& request)
        : request_(request)
    {
    }
    ~Job() override
    {
        if (request_->factory_) {
            request_->factory_->cancelled_requests_.push_back(request_);
            request_->factory_->RemovePendingRequest(request_.get());
        }
    }

private:
    scoped_refptr<MockAsyncProxyResolverFactory::Request> request_;
};

MockAsyncProxyResolverFactory::MockAsyncProxyResolverFactory(
    bool resolvers_expect_pac_bytes)
    : ProxyResolverFactory(resolvers_expect_pac_bytes)
{
}

int MockAsyncProxyResolverFactory::CreateProxyResolver(
    const scoped_refptr<ProxyResolverScriptData>& pac_script,
    std::unique_ptr<ProxyResolver>* resolver,
    const net::CompletionCallback& callback,
    std::unique_ptr<ProxyResolverFactory::Request>* request_handle)
{
    scoped_refptr<Request> request = new Request(this, pac_script, resolver, callback);
    pending_requests_.push_back(request);

    request_handle->reset(new Job(request));

    // Test code completes the request by calling request->CompleteNow().
    return ERR_IO_PENDING;
}

void MockAsyncProxyResolverFactory::RemovePendingRequest(Request* request)
{
    RequestsList::iterator it = std::find(pending_requests_.begin(), pending_requests_.end(), request);
    DCHECK(it != pending_requests_.end());
    pending_requests_.erase(it);
}

MockAsyncProxyResolverFactory::~MockAsyncProxyResolverFactory()
{
    for (auto& request : pending_requests_) {
        request->FactoryDestroyed();
    }
}

ForwardingProxyResolver::ForwardingProxyResolver(ProxyResolver* impl)
    : impl_(impl)
{
}

int ForwardingProxyResolver::GetProxyForURL(const GURL& query_url,
    ProxyInfo* results,
    const CompletionCallback& callback,
    RequestHandle* request,
    const BoundNetLog& net_log)
{
    return impl_->GetProxyForURL(query_url, results, callback, request, net_log);
}

void ForwardingProxyResolver::CancelRequest(RequestHandle request)
{
    impl_->CancelRequest(request);
}

LoadState ForwardingProxyResolver::GetLoadState(RequestHandle request) const
{
    return impl_->GetLoadState(request);
}

} // namespace net
